package org.terramagnet.http.signature.impl;

import org.terramagnet.http.Connection;
import org.terramagnet.http.signature.SignException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * md5 签名逻辑.
 *
 * @author lip
 */
public class Md5SignatureAlgorithm extends SortedSignatureAlgorithm {

	private final Logger logger = LoggerFactory.getLogger(Md5SignatureAlgorithm.class);

	@Override
	public String signSortedFields(TreeSet<Connection.KeyVal> parameters) {
		StringBuilder builder = new StringBuilder();
		for (Connection.KeyVal parameter : parameters) {
			builder.append(parameter.key())
				.append("=")
				.append(parameter.value())
				.append("&");
		}
		if (!parameters.isEmpty()) {
			builder.deleteCharAt(builder.length() - 1);
		}
		builder.append(secret());
		logger.trace("before sign: {}", builder);
		try {
			MessageDigest instance = MessageDigest.getInstance("MD5");
			byte[] digest = instance.digest(builder.toString().getBytes("UTF-8"));
			String hex = new String(encodeHex(digest));
			logger.trace("after sign: {}", hex);
			return hex;
		} catch (NoSuchAlgorithmException ex) {
			throw new SignException(ex);
		} catch (UnsupportedEncodingException ex) {
			throw new SignException(ex);
		}
	}

	/**
	 * Converts an array of bytes into an array of characters representing the
	 * hexadecimal values of each byte in order. The returned array will be
	 * double the length of the passed array, as it takes two characters to
	 * represent any given byte.
	 *
	 * <p>
	 * copied from commons-codec</p>
	 *
	 * @param data a byte[] to convert to Hex characters
	 * @param toDigits the output alphabet
	 * @return A char[] containing hexadecimal characters
	 */
	private static char[] encodeHex(byte[] data) {
		int l = data.length;
		char[] out = new char[l << 1];
		// two characters form the hex value.
		for (int i = 0, j = 0; i < l; i++) {
			out[j++] = DIGITS_LOWER[(0xF0 & data[i]) >>> 4];
			out[j++] = DIGITS_LOWER[0x0F & data[i]];
		}
		return out;
	}
	public static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

}
